"""
媒体目录扫描器

专门用于扫描媒体文件（视频、音频、图片）的目录扫描器。
"""

import asyncio
import os
import mimetypes
from typing import Dict, List, Any, Set, Optional
from pathlib import Path
from datetime import datetime
import logging

from src.core.di import Injectable, Inject
from .base import DirectoryScanner, ScanResult, FileInfo, FileType


@Injectable(scope="singleton")
class MediaDirectoryScanner(DirectoryScanner):
    """
    媒体目录扫描器
    
    专门扫描和识别媒体文件，提取基本文件信息。
    """
    
    def __init__(self,
                 config: Dict[str, Any] = Inject("config"),
                 logger: logging.Logger = Inject("logger")):
        self.config = config
        self.logger = logger
        
        # 扫描配置
        self.scanner_config = config.get("scanner", {})
        self.max_concurrent_scans = self.scanner_config.get("max_concurrent_scans", 10)
        self.include_hidden = self.scanner_config.get("include_hidden", False)
        self.follow_symlinks = self.scanner_config.get("follow_symlinks", False)
        
        # 支持的文件扩展名
        self.video_extensions = {
            "mp4", "avi", "mov", "mkv", "webm", "flv", "wmv", "m4v",
            "3gp", "asf", "rm", "rmvb", "vob", "ts", "mts", "m2ts"
        }
        
        self.audio_extensions = {
            "mp3", "wav", "aac", "flac", "ogg", "wma", "m4a", "ac3",
            "dts", "ape", "opus", "amr", "aiff"
        }
        
        self.image_extensions = {
            "jpg", "jpeg", "png", "gif", "bmp", "tiff", "tif", "webp",
            "svg", "ico", "psd", "raw", "cr2", "nef", "arw"
        }
        
        # 初始化MIME类型
        mimetypes.init()
    
    def get_supported_extensions(self) -> Set[str]:
        """获取支持的文件扩展名"""
        return self.video_extensions | self.audio_extensions | self.image_extensions
    
    async def scan(self, directory: str, **kwargs) -> ScanResult:
        """扫描目录"""
        self.logger.info(f"开始扫描目录: {directory}")
        
        start_time = datetime.now()
        scan_result = ScanResult(
            scan_path=directory,
            scan_time=start_time
        )
        
        try:
            # 检查目录是否存在
            dir_path = Path(directory)
            if not dir_path.exists():
                raise FileNotFoundError(f"目录不存在: {directory}")
            
            if not dir_path.is_dir():
                raise NotADirectoryError(f"路径不是目录: {directory}")
            
            # 获取扫描选项
            recursive = kwargs.get("recursive", True)
            file_patterns = kwargs.get("file_patterns", ["*"])
            max_depth = kwargs.get("max_depth", None)
            
            # 收集文件
            files_to_scan = self._collect_files(
                dir_path, recursive, file_patterns, max_depth
            )
            
            scan_result.total_files = len(files_to_scan)
            self.logger.info(f"找到 {len(files_to_scan)} 个文件")
            
            # 并发扫描文件
            file_infos = await self._scan_files_concurrent(files_to_scan)
            
            # 过滤和统计
            for file_info in file_infos:
                if file_info:
                    scan_result.files.append(file_info)
                    scan_result.processed_files += 1
                    
                    # 统计文件类型
                    file_type = file_info.file_type.value
                    scan_result.type_stats[file_type] = scan_result.type_stats.get(file_type, 0) + 1
                    
                    # 统计分类
                    category = file_info.category.value
                    scan_result.category_stats[category] = scan_result.category_stats.get(category, 0) + 1
                else:
                    scan_result.error_files += 1
            
            # 计算扫描时长
            end_time = datetime.now()
            scan_result.scan_duration = (end_time - start_time).total_seconds()
            
            self.logger.info(f"扫描完成: 处理 {scan_result.processed_files}/{scan_result.total_files} 个文件")
            
        except Exception as e:
            error_msg = f"扫描目录失败: {e}"
            self.logger.error(error_msg)
            scan_result.errors.append(error_msg)
        
        return scan_result
    
    def _collect_files(self, 
                      dir_path: Path, 
                      recursive: bool, 
                      file_patterns: List[str],
                      max_depth: Optional[int]) -> List[Path]:
        """收集需要扫描的文件"""
        import fnmatch
        
        files = []
        
        def should_include_file(file_path: Path) -> bool:
            # 检查隐藏文件
            if not self.include_hidden and file_path.name.startswith('.'):
                return False
            
            # 检查文件模式
            if file_patterns and file_patterns != ["*"]:
                name_match = any(fnmatch.fnmatch(file_path.name.lower(), pattern.lower()) 
                               for pattern in file_patterns)
                if not name_match:
                    return False
            
            # 检查扩展名
            return self.is_supported_file(str(file_path))
        
        def scan_directory(current_dir: Path, current_depth: int = 0):
            try:
                for item in current_dir.iterdir():
                    # 检查深度限制
                    if max_depth is not None and current_depth >= max_depth:
                        continue
                    
                    if item.is_file():
                        if should_include_file(item):
                            files.append(item)
                    elif item.is_dir() and recursive:
                        # 检查符号链接
                        if item.is_symlink() and not self.follow_symlinks:
                            continue
                        
                        # 检查隐藏目录
                        if not self.include_hidden and item.name.startswith('.'):
                            continue
                        
                        scan_directory(item, current_depth + 1)
                        
            except PermissionError:
                self.logger.warning(f"无权限访问目录: {current_dir}")
            except Exception as e:
                self.logger.error(f"扫描目录时出错 {current_dir}: {e}")
        
        scan_directory(dir_path)
        return files
    
    async def _scan_files_concurrent(self, files: List[Path]) -> List[Optional[FileInfo]]:
        """并发扫描文件"""
        semaphore = asyncio.Semaphore(self.max_concurrent_scans)
        
        async def scan_single_file(file_path: Path) -> Optional[FileInfo]:
            async with semaphore:
                try:
                    return await self._scan_single_file(file_path)
                except Exception as e:
                    self.logger.error(f"扫描文件失败 {file_path}: {e}")
                    return None
        
        # 创建并发任务
        tasks = [scan_single_file(file_path) for file_path in files]
        
        # 等待所有任务完成
        return await asyncio.gather(*tasks, return_exceptions=False)
    
    async def _scan_single_file(self, file_path: Path) -> FileInfo:
        """扫描单个文件"""
        try:
            # 获取文件统计信息
            stat = file_path.stat()
            
            # 创建文件信息
            file_info = FileInfo(
                path=str(file_path.absolute()),
                name=file_path.name,
                size=stat.st_size,
                created_time=datetime.fromtimestamp(stat.st_ctime),
                modified_time=datetime.fromtimestamp(stat.st_mtime),
                extension=file_path.suffix.lower().lstrip('.')
            )
            
            # 确定文件类型
            file_info.file_type = self._determine_file_type(file_info.extension)
            
            # 获取MIME类型
            mime_type, _ = mimetypes.guess_type(str(file_path))
            file_info.mime_type = mime_type or ""
            
            # 添加基本元数据
            file_info.metadata = {
                "file_size_mb": file_info.size / (1024 * 1024),
                "file_age_days": (datetime.now() - file_info.modified_time).days,
                "directory": str(file_path.parent),
                "relative_path": str(file_path)
            }
            
            return file_info
            
        except Exception as e:
            self.logger.error(f"处理文件失败 {file_path}: {e}")
            raise
    
    def _determine_file_type(self, extension: str) -> FileType:
        """确定文件类型"""
        if extension in self.video_extensions:
            return FileType.VIDEO
        elif extension in self.audio_extensions:
            return FileType.AUDIO
        elif extension in self.image_extensions:
            return FileType.IMAGE
        else:
            return FileType.UNKNOWN
    
    async def scan_with_metadata(self, directory: str, **kwargs) -> ScanResult:
        """扫描目录并提取详细元数据"""
        # 首先进行基础扫描
        scan_result = await self.scan(directory, **kwargs)
        
        # 如果启用了元数据提取
        if kwargs.get("extract_metadata", False):
            await self._extract_metadata_for_files(scan_result.files)
        
        return scan_result
    
    async def _extract_metadata_for_files(self, files: List[FileInfo]) -> None:
        """为文件提取详细元数据"""
        try:
            from src.services.metadata_service import MetadataService
            from src.bootstrap import get_service
            
            metadata_service = get_service(MetadataService)
            
            # 只处理视频和音频文件
            media_files = [f for f in files if f.file_type in [FileType.VIDEO, FileType.AUDIO]]
            
            if not media_files:
                return
            
            self.logger.info(f"提取 {len(media_files)} 个媒体文件的元数据")
            
            # 批量提取元数据
            file_paths = [f.path for f in media_files]
            metadata_results = await metadata_service.batch_extract_metadata(file_paths)
            
            # 更新文件信息
            for file_info in media_files:
                metadata = metadata_results.get(file_info.path)
                if metadata:
                    # 添加详细元数据
                    if hasattr(metadata, 'duration'):
                        file_info.metadata['duration'] = metadata.duration
                    if hasattr(metadata, 'width') and hasattr(metadata, 'height'):
                        file_info.metadata['resolution'] = f"{metadata.width}x{metadata.height}"
                    if hasattr(metadata, 'fps'):
                        file_info.metadata['fps'] = metadata.fps
                    if hasattr(metadata, 'video_codec'):
                        file_info.metadata['video_codec'] = metadata.video_codec
                    if hasattr(metadata, 'audio_codec'):
                        file_info.metadata['audio_codec'] = metadata.audio_codec
                    
                    file_info.is_processed = True
                else:
                    file_info.processing_errors.append("元数据提取失败")
                    
        except Exception as e:
            self.logger.error(f"批量元数据提取失败: {e}")
            for file_info in files:
                if file_info.file_type in [FileType.VIDEO, FileType.AUDIO]:
                    file_info.processing_errors.append(f"元数据提取失败: {e}")
